package com.gp.framework.context;

import com.gp.framework.annotation.GPAutowired;
import com.gp.framework.annotation.GPController;
import com.gp.framework.annotation.GPService;
import com.gp.framework.proxy.LogInterceptor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * IOC容器
 *
 * @author
 * @create 2017-06-21
 */
public class GPApplicationContext {
    private static final Logger LOGGER = LogManager.getLogger(GPApplicationContext.class);

    /** key: 类名首字母小写（GPController）、接口的完整限定名（GPService） value：实例对象 */
    private Map<String, Object> instanceMap = new ConcurrentHashMap<String, Object>();

    /** 类的完整限定名集合 */
    private List<String> classCache = new ArrayList<String>();

    public GPApplicationContext(String location) {
        //1、定位
        try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(location)) {
            //2、载入
            Properties config = new Properties();
            config.load(is);

            //3、注册，把所有class找出来存着
            String packageName = config.getProperty("scanPackage");
            doRegister(packageName);

            //4、实例化需要ioc的对象(就是加了@Service、@Controller)，只要循环class了
            doCreateBean();

            //5、注入
            populate();
        } catch (Exception e) {
            LOGGER.error("IOC容器初始化异常, {}", e);
        }
        LOGGER.info("{}", "IOC容器已经初始化");
    }

    //把符合条件所有的class全部找出来，注册到缓存里面去
    private void doRegister(String packageName) {
        URL url = this.getClass().getResource("/" + packageName.replaceAll("\\.", "/"));
        File dir = new File(url.getFile());
        for (File file : dir.listFiles()) {
            //如果是一个文件夹，继续递归
            if (file.isDirectory()) {
                doRegister(packageName + "." + file.getName());
            } else {
                classCache.add(packageName + "." + file.getName().replace(".class", "").trim());
            }
        }
    }

    //实例化对象
    private void doCreateBean() {
        //检查看有没有注册信息,注册信息里面保存了所有的class名字
        if (classCache.size() == 0) {
            return;
        }

        try {
            for (String className : classCache) {
                //知道这里有一个套路  jdk动态代理、CGLIB
                Class<?> clazz = Class.forName(className);

                //那个类需要初始化，哪个类不要初始化
                //只要加了  @Service  @Controller都要初始化
                if (clazz.isAnnotationPresent(GPController.class)) {
                    //默认就是类名首字母小写
                    String id = lowerFirstChar(clazz.getSimpleName());
                    instanceMap.put(id, clazz.newInstance());
                } else if (clazz.isAnnotationPresent(GPService.class)) {
                    GPService service = clazz.getAnnotation(GPService.class);

                    //如果设置了自定义名字，就优先用他自己定义的名字
                    String id = service.value();
                    if (!"".equals(id.trim())) {
                        instanceMap.put(id, clazz.newInstance());
                        continue; // 这里使用，避免下面使用else嵌套
                    }

                    Class<?>[] interfaces = clazz.getInterfaces();
                    //如果这个类实现了接口，就用接口的类型作为id
                    for (Class<?> theInterface : interfaces) {
                        //完整限定名，避免接口名字重复
                        instanceMap.put(theInterface.getName(), clazz.newInstance());
                    }
                } else {
                    continue;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //依赖注入
    private void populate() {
        //首先要判断ioc容器中是否存在实例，如果不存在，则不需要进行依赖注入
        if (instanceMap.isEmpty()) {
            return;
        }

        for (Entry<String, Object> entry : instanceMap.entrySet()) {
            //把所有的属性全部取出来，包括私有属性
            Field[] fields = entry.getValue().getClass().getDeclaredFields();

            for (Field field : fields) {
                if (!field.isAnnotationPresent(GPAutowired.class)) {
                    continue;
                }

                GPAutowired autowired = field.getAnnotation(GPAutowired.class);

                String autowiredValue = autowired.value().trim();
                //如果id为空，也就是说，自己没有设置，默认根据类型来注入
                if ("".equals(autowiredValue)) {
                    autowiredValue = field.getType().getName(); //完整限定名
                }
                field.setAccessible(true); //把私有变量开放访问权限

                try {
                    field.set(entry.getValue(), instanceMap.get(autowiredValue));
                } catch (Exception e) {
                    e.printStackTrace();
                    continue;
                }
            }
        }
    }

    //将首字母小写
    private String lowerFirstChar(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    public Map<String, Object> getAllInstances() {
        return instanceMap;
    }

    //获取bean的代理实例
    public Object getProxyBean(String beanName) {
        Object obj = instanceMap.get(beanName);
        return new LogInterceptor().getInstance(obj.getClass());
    }
}
